home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Interactive Reference Guide
/
C-C++ Interactive Reference Guide.iso
/
c_ref
/
csource5
/
357_01
/
cstar1.exe
/
PH.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-06-18
|
15KB
|
714 lines
/*
C* Peephole optimizer.
source: ph.c
started: October 26, 1985
version:
February 22, 1989
PUBLIC DOMAIN SOFTWARE
The CSTAR program was placed in the public domain on June 15, 1991,
by its author and sole owner,
Edward K. Ream
1617 Monroe Street
Madison, WI 53711
(608) 257-0802
CSTAR may be used for any commercial or non-commercial purpose.
See cstar.h or cstar.c for a DISCLAIMER OF WARRANTIES.
*/
#include "cstar.h"
/*
Declare routines of this file.
*/
void peep_hole (void);
static void c_delete (register struct node *p);
static struct node * peep0 (register struct node *p);
static struct node * peep1 (register struct node *p);
static struct node * peep2 (register struct node *p);
static struct node * peep3 (register struct node *p);
static struct node * peep4 (register struct node *p);
static struct node * peep5 (register struct node *p);
static struct node * dec_ref (register struct node *p);
static int bxx2byy (int code);
/*
----- P E E P H O L E R O U T I N E S ----
*/
/*
Unlink node p from doubly-linked code list.
*/
static void
c_delete(register struct node *p)
{
TRACEP("c_delete", printf("(%p)\n", p));
if (p == NULL) {
return;
}
/* Remove the forward link to p. */
if (p -> c_back == NULL) {
code_head = p -> c_next;
}
else {
(p -> c_back) -> c_next = p -> c_next;
}
/* Remove the back link to p. */
if (p -> c_next == NULL) {
code_tail = p -> c_back;
}
else {
(p -> c_next) -> c_back = p -> c_back;
}
}
/*
Peephole optimizer loop.
All subsidiary routines must set changed as follows:
changed: TRUE if any optimization was performed.
This almost always finishes in two passes over the code.
*/
static int changed;
static int advanced;
static int pass;
void
peep_hole(void)
{
register struct node *p;
register int g_chg;
TRACE("nopeep", printf("\n>> Disabling Peep Hole...\n\n"); return;);
TRACE("peep_hole", printf("\n>> Peep Hole Optimizer...\n\n"));
pass = 0;
do {
g_chg = FALSE;
changed = FALSE;
pass++;
p = code_head;
TRACEP("peep_hole", printf("p=%p, pass %d\n\n", p, pass));
while (p) {
if (changed) {
g_chg = TRUE;
/* Try to avoid multiple passes. */
if (p -> c_back) {
p = p -> c_back;
}
}
changed = FALSE;
p = peep0(p); if (changed) {continue;}
p = peep1(p); if (changed) {continue;}
p = peep2(p); if (changed) {continue;}
p = peep3(p); if (changed) {continue;}
p = peep4(p); if (changed) {continue;}
p = peep5(p); if (changed) {continue;}
p = p -> c_next;
}
} while (g_chg);
}
/*
Peephole optimization 0
X_CLR: convert clr.l dx to moveq.l #0,dx
X_MOVE: delete move to self--compiler never uses flags based on this
convert long constant move into A to int if possible
X_TST: delete test following move or arithmetic (move to self
deleted first) provided that argument matches. MOVEA
is no problem since TST A doesn't work anyhow
X_ADD/ADDA: if constant is added to areg, and const is over 8
and within -32768 to +32767, convert to LEA.
X_OLABEL: delete if reference count is 0
Eliminate redundant moves and tests
*/
static struct node *
peep0(register struct node *p)
{
register struct node *q, *t;
register struct node *arg1;
register long value;
TRACEP("peep0v", printf("(%p)\n", p));
switch(p -> c_code) {
case X_CLR:
if (pass == 1) {
if (p -> c_len1 == 4 && is_dloc(p -> c_arg1)) {
p -> c_arg2 = p -> c_arg1;
p -> c_arg1 = zero_loc;
p -> c_code = X_MOVEQ;
}
}
break;
case X_MOVE:
/*
NOTE: we never use move-to-self to set the flags, since
it is useless for that purpose. In any event, if such
a move did precede an X_TST, it would be gone before
the attempt to remove the X_TST was completed.
*/
if (pass == 1) {
arg1 = p -> c_arg1;
if (is_equiv(arg1, p -> c_arg2) &&
!has_effect(arg1 -> n_mode)) {
q = p -> c_next;
c_delete(p);
changed = TRUE;
return q;
}
if (is_zloc(arg1) && !is_aloc(p -> c_arg2)) {
p -> c_arg1 = p -> c_arg2;
p -> c_arg2 = NULL;
p -> c_code = X_CLR;
}
else if (p -> c_len1 == 4 && is_aloc(p -> c_arg2) &&
is_cloc(arg1)) {
if (!arg1 -> n_cid && arg1 -> n_const < 32767 &&
arg1 -> n_const >= -32768L) {
TRACEP("peep0",
printf("change a0 long move"));
p -> c_len1 = 2;
}
}
}
break;
case X_TST:
/* note that X_TST is not applied to an aloc */
/* note that X_MOVE and X_ADD might not set the flags */
arg1 = p -> c_arg1;
q = p;
while (q = q -> c_back) {
switch(q -> c_code) {
case O_LINENUM:
continue;
/* labels should NOT be skipped! */
case X_MOVE:
case X_ADD:
case X_SUB:
case X_EOR:
case X_OR:
case X_AND:
case X_MULS:
case X_MULU:
case X_DIVS:
case X_DIVU:
case X_EXT:
TRACEP("peep0",
printf("X_TST binary: ");
pr_loc(arg1);
printf(","); pr_loc(q -> c_arg2);
printf("; ");
printf("%d,%d\n", p -> c_len1,
q -> c_len1);
);
if (is_equiv(arg1, q -> c_arg2)) {
if (p -> c_len1 != q -> c_len1) {
break;
}
q = p -> c_next;
c_delete(p);
changed = TRUE;
return q;
}
break;
case X_NEG:
case X_NOT:
case X_TST:
if (is_equiv(arg1, q -> c_arg1)) {
if (p -> c_len1 != q -> c_len1) {
break;
}
q = p -> c_next;
c_delete(p);
changed = TRUE;
return q;
}
break;
}
break;
}
break;
case O_LABEL:
if (p -> c_refcount == 0) {
changed = TRUE;
TRACEP("peep0", printf("remove label: %p\n", p));
q = p -> c_next;
c_delete(p);
return q;
}
break;
case X_ADD:
case X_ADDA:
if (pass != 1) {
break;
}
arg1 = p -> c_arg1;
if (is_aloc(p -> c_arg2) &&
!arg1 -> n_cid && is_cloc(arg1)) {
}
else {
break;
}
TRACEP("peep0", printf("check constant ADDA %p\n", p));
value = arg1 -> n_const;
if (value < 0) {
if (value >= -8) {
p -> c_code = X_SUBQ;
arg1 -> n_const = -value;
}
else if (value >= -32768) {
p -> c_len1 = 0;
p -> c_code = X_LEA;
arg1 -> n_mode = EA_MODE;
arg1 -> n_reg1 =
p -> c_arg2 -> n_reg1;
}
}
else if (value == 0) {
q = p -> c_next;
changed = TRUE;
c_delete(p);
return q;
}
else {
if (value <= 8) {
p -> c_code = X_ADDQ;
}
else if (value <= 32767) {
p -> c_len1 = 0;
p -> c_code = X_LEA;
arg1 -> n_mode = EA_MODE;
arg1 -> n_reg1 =
p -> c_arg2 -> n_reg1;
}
}
break;
}
return p;
}
/*
Peephole optimization 1
Merge adjacent internal labels.
*/
static struct node *
peep1(register struct node *p1)
{
register int count, type;
register struct node *p2, *q;
TRACEP("peep1v", printf("(%p)\n", p1));
/* Internal label node ? */
if (p1 -> c_code != O_LABEL) {
return p1;
}
p2 = p1 -> c_next;
while (p2 && p2 -> c_code == O_LINENUM) {
p2 = p2 -> c_next;
}
if (p2 == NULL || p2 -> c_code != O_LABEL) {
return p1;
}
/* Replace all references to p1 by references to p2. */
count = 0;
for (q = code_head; q; q = q -> c_next) {
type = q -> c_code;
if (type == X_BRA || is_bxx(type)) {
if (q -> c_arg1 == p1) {
count++;
q -> c_arg1 = p2;
}
}
}
if (count != p1 -> c_refcount) {
fatal("peep1: mismatched reference counts");
}
/* Do the optimization. */
TRACEP("peep1", printf("merge adjacent labels: %p, %p\n", p1, p2));
changed = TRUE;
(p2 -> c_refcount) += count;
c_delete(p1);
return p2;
}
/*
Peephole optimization 2
Replace bxx lab1
...
lab1: bra lab2
...
lab2:
by
bxx lab2
...
lab1: bra lab2
...
lab2:
Increment the refcount of lab2 and decrement refcount of lab1.
Eliminate lab1 if its reference count falls to zero.
Cycles of jumps in this context indicate potential infinite loops;
if the cycle closes, there is no way out.
TERMINATION PROOF: if this optimization encounters a closed cycle,
it fails and generates an error. If it encounters an open cycle,
it carries the branch reference forward to the end of the cycle,
and thus eliminates the cycle, and will not succeed again at that
same place.
At the present time, all other optimizations remove some code,
so restoring a pattern visible to this one requires removal
of code and must terminate. This does not rule out the possibility
of a cyclic interaction between this one and some future optimizer
that only rearranges code.
*/
static struct node *
peep2(register struct node *p)
{
register struct node *q, *target;
register int type;
TRACEP("peep2v", printf("(%p)\n", p));
#ifdef DEBUG
if (p == NULL) {
g_error(NULL, "peep2: shouldn't happen");
return p;
}
#endif /* DEBUG */
type = p -> c_code;
if (type != X_BRA && !is_bxx(type)) {
return p;
}
/* the current node is a jump of some sort */
target = NULL;
q = p -> c_arg1;
while (q) {
TRACEP("peep2v", printf("mark: q=%p\n", q));
#ifdef DEBUG
type = q -> c_code;
if (type != O_LABEL && type != O_ULABEL) {
fatal("peep2: jump to a non-label");
}
#endif /* DEBUG */
/* Check for cycle and mark the label node. */
if (q -> c_mark) {
/* Disable optimization. */
g_help(NULL, "closed cycle of branches!");
target = NULL;
break;
}
else {
q -> c_mark = TRUE;
}
/* See if the next instruction is an unconditional branch. */
do {
q = q -> c_next;
}
while (q && q -> c_code == O_LINENUM);
if (q == NULL || (q -> c_code != X_BRA)) {
break;
}
/* We have (another) jump to an unconditional jump. */
target = q = q -> c_arg1;
TRACEP("peep2", printf("new target=%p\n", target));
}
/* Unmark all the marked nodes before the optimization. */
for (q = p -> c_arg1; q && q -> c_mark ;q = q -> c_arg1) {
TRACEP("peep2v", printf("unmark: q=%p\n", q));
q -> c_mark = FALSE;
do {
q = q -> c_next;
}
while (q && q -> c_code == O_LINENUM);
if (q == NULL || (q -> c_code != X_BRA)) {
break;
}
}
/* Do the optimization. */
if (target) {
TRACEP("peep2",
printf("retarget branch: %p to %p\n", p, target));
(void) dec_ref(p -> c_arg1);
p -> c_arg1 = target;
target -> c_refcount++;
changed = TRUE;
}
/* This optimization never alters the code pointer. */
return p;
}
/*
Peephole optimization 3--flip polarity of certain conditional branches
Replace bxx lab1
bra lab2
lab1:
by
byy lab2
lab1:
Also adjust and possibly remove lab1.
*/
static struct node *
peep3(register struct node *p)
{
register struct node *q1, *q2;
TRACEP("peep3v", printf("(%p)\n", p));
/* Conditional jump */
if (!is_bxx(p -> c_code)) {
return p;
}
/* Followed by an unconditional jump? */
q1 = p -> c_next;
while (q1 && q1 -> c_code == O_LINENUM) {
q1 = q1 -> c_next;
}
if (q1 == NULL || q1 -> c_code != X_BRA) {
return p;
}
/* Followed by the target of the first jump? */
q2 = q1 -> c_next;
if (q2 == NULL || q2 != p -> c_arg1) {
return p;
}
/* Do the optimization. */
TRACEP("peep3",
printf("switching bxx polarity: %p\n", p);
printf("p=%p, q1=%p, q2=%p\n", p, q1, q2));
changed = TRUE;
p -> c_code = bxx2byy(p -> c_code);
p -> c_arg1 = q1 -> c_arg1;
c_delete(q1);
(void) dec_ref(q2);
return p;
}
/*
Peephole optimization 4--delete jump to immediately following label
Replace jmp lab1
lab1:
by lab1:
Decrement the reference count of lab1 and eliminate lab1 if
its reference count falls to zero.
*/
static struct node *
peep4(register struct node *p)
{
register int type;
register struct node *q;
TRACEP("peep4v", printf("(%p)\n", p));
type = p -> c_code;
if (type != X_BRA && !is_bxx(type)) {
return p; /* it's not a jump */
}
/* Does the jump go to the next instruction? */
q = p -> c_next;
while (q && q -> c_code == O_LINENUM) {
q = q -> c_next;
}
if (q && /* (struct node *) */ p -> c_arg1 == q) {
TRACEP("peep4",
printf("remove jump to next: %p\n", p);
printf("p=%p, q=%p\n", p, q));
c_delete(p); /* delete the jump */
q = dec_ref(q); /* and maybe the label as well */
changed = TRUE;
return q;
}
else {
return p;
}
}
/*
Peephole optimization 5--remove unreachable code
i.e., all code between an unconditional jump and
the next label having any references
*/
static struct node *
peep5(register struct node *p)
{
register struct node *q;
register int type;
TRACEP("peep5v", printf("(%p)\n", p));
/* Do we have an unconditional jump? */
if (p -> c_code != X_BRA) {
return p;
}
/* Remove the next instruction until it is a label with references */
/* Adjust reference counts when removing branches! */
q = p -> c_next;
while (q) {
type = q -> c_code;
if (type == X_BRA || is_bxx(type)) {
(void) dec_ref(q -> c_arg1);
}
else if (type == O_LABEL || type == O_ULABEL) {
if (q -> c_refcount) {
TRACEP("peep5v", printf("label found, stop\n"));
break;
}
}
TRACEP("peep5", printf("remove unreachable code: %p\n", q));
changed = TRUE;
c_delete(q);
q = q -> c_next;
}
return p;
}
/*
Return the negation of a conditional jump code.
*/
static int
bxx2byy(int code)
{
TRACEP("bxx2byy", printf("(%d)\n", code));
switch(code) {
case X_BEQ: return X_BNE;
case X_BNE: return X_BEQ;
case X_BLT: return X_BGE;
case X_BGE: return X_BLT;
case X_BGT: return X_BLE;
case X_BLE: return X_BGT;
case X_BHI: return X_BLS; /* Note */
case X_BLS: return X_BHI;
case X_BCC: return X_BCS;
case X_BCS: return X_BCC;
case X_BVC: return X_BVS;
case X_BVS: return X_BVC;
case X_BPL: return X_BMI;
case X_BMI: return X_BPL;
default:
printf("bxx2byy: bad code %d\n", code);
return code;
}
}
/*
Decrement the referece count of a label node and
remove the label if it falls to zero.
Return the pointer to the label node or the following node
if the label node was eliminated.
*/
static struct node *
dec_ref(register struct node * p)
{
register struct node *q;
TRACEP("dec_ref", printf("(%p)\n", p));
#ifdef DEBUG
if ( (p == NULL) ||
(p -> c_code != O_LABEL && p -> c_code != O_ULABEL) ||
(p -> c_refcount) <= 0
) {
fatal("decref: shouldn't happen");
}
#endif
p -> c_refcount--;
if (p -> c_refcount == 0) {
changed = TRUE;
TRACE("dec_ref", printf("remove label: %p\n", p));
q = p -> c_next;
c_delete(p);
return q;
}
else {
return p;
}
}